﻿using FluentFTP;
using HAF.LogUtility;
using HAF.LogUtility.Except;
using HAF.LogUtility.Model;
using Iptv.DownResourceItem.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;

namespace Iptv.DownResourceItem.Helper
{
    /// <summary>
    /// FluentFtp下载组件
    /// </summary>
    public class FluentFtpHelper : PushMessageable, IDisposable
    {
        #region 运行参数
        Guid currentFileId = Guid.Empty;
        /// <summary>  
        /// FTP站点IP地址
        /// </summary>  
        public string Host { get; private set; }
        /// <summary>
        /// FTP站点端口
        /// </summary>
        public int Port { get; private set; }
        /// <summary>  
        /// FTP站点默认目录
        /// </summary>  
        public string RemotePath { get; private set; }
        /// <summary>  
        /// FTP站点登录用户名
        /// </summary>  
        public string UserID { get; private set; }
        /// <summary>  
        /// FTP站点登录密码
        /// </summary>  
        public string Password { get; private set; }
        /// <summary>
        /// FTP访问编码
        /// </summary>
        public string Encoder { get; private set; }
        #endregion 运行参数

        private FtpClient _ftpClient;

        #region 外部扩展事件
        /// <summary>
        /// 文件上传进度输出
        /// </summary>
        public Action<FtpProgress> UploadProgressChangeEvent;
        /// <summary>
        /// 文件上传完成事件输出
        /// </summary>
        public Action<string, bool> UploadEndEvent;

        /// <summary>
        /// 文件下载进度输出
        /// </summary>
        public Action<string, int> DownloadProcessChangeEvent;
        /// <summary>
        /// 文件下载状态输出
        /// </summary>
        public Action<string, long, long, long, long> DownloadChangeEvent;
        /// <summary>
        /// 文件下载完成事件输出
        /// </summary>
        public Action<string, int> DownloadEndEvent;

        #endregion 外部扩展事件

        #region 构造函数
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="siteConfig">站点配置</param>
        public FluentFtpHelper(SiteConfig siteConfig) : this(siteConfig.IP, siteConfig.Port, "/", siteConfig.UserId, siteConfig.Password, siteConfig.Encoding, siteConfig.TimeOut) { }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ftpServerIP">地址</param>
        /// <param name="ftpPort">端口</param>
        /// <param name="ftpRemotePath">远端文件全路径</param>
        /// <param name="ftpUserID">账号</param>
        /// <param name="ftpPassword">密码</param>
        /// <param name="ftpEncoding">字符集</param>
        /// <param name="timeOut">超时时长</param>
        /// <exception cref="RunException">异常抛出</exception>
        public FluentFtpHelper(string ftpServerIP, int ftpPort, string ftpRemotePath, string ftpUserID, string ftpPassword, string ftpEncoding, int timeOut)
        {
            this.currentFileId = Guid.NewGuid();
            this.Host = ftpServerIP;
            this.Port = ftpPort;
            this.RemotePath = ftpRemotePath;
            this.UserID = ftpUserID;
            this.Password = ftpPassword;
            this.Encoder = string.IsNullOrEmpty(ftpEncoding) ? "utf8" : ftpEncoding;

            if (string.IsNullOrEmpty(ftpRemotePath))
            {
                this.RemotePath = "ftp://" + ftpServerIP + ":" + ftpPort + "/";
            }
            else
            {
                this.RemotePath = "ftp://" + ftpServerIP + ":" + ftpPort + "/" + ftpRemotePath + "/";
            }
            try
            {
                _ftpClient = new FtpClient(Host);
                _ftpClient.Port = Port;
                _ftpClient.Credentials = new NetworkCredential(UserID, Password);
                _ftpClient.Encoding = this.Encoder.ToLower().Equals("utf8") ? Encoding.UTF8 : Encoding.Default;
                //设置30分钟的超时时间 30 * 60 * 1000; 4K视频太大，下载时间很长
                //_ftpClient.ConnectTimeout = timeOut == 0 ? 10000 : timeOut;
                _ftpClient.Config.ConnectTimeout = timeOut == 0 ? 10000 : timeOut; ;
                //保持链接
                _ftpClient.Config.SocketKeepAlive = true;

            }
            catch (Exception ex)
            {
                WriteErrMessage(ex.ToString());
                throw new RunException((int)TransferCode.AuthFailed, ex.ToString());
            }

        }
        #endregion 构造函数

        /// <summary>
        /// 文件下载
        /// </summary>
        /// <param name="saveFile">本地存储文件全路径</param>
        /// <param name="remoteFile">远端文件全路径</param>
        /// <param name="threadId">线程编号</param>
        /// <returns></returns>
        public ResponseLog DownFile(string saveFile, string remoteFile, string threadId = "")
        {
            PushInfoLog(LogOutName, "启动FluentFtp下载{0}", remoteFile);
            ResponseLog result = new ResponseLog() { Code = (int)TransferCode.Success, Message = "成功" };

            if (_ftpClient == null || _ftpClient.IsDisposed)
            {
                return new ResponseLog() { Code = (int)TransferCode.Error, Message = "FTP未准备好" };
            }
            WriteDebugMessage("检查本地下载目录");
            saveFile = saveFile.Replace("/", "\\");
            //本地文件存储目录
            string saveDir = saveFile.Substring(0, saveFile.LastIndexOf(@"\"));
            if (!Directory.Exists(saveDir))
            {
                Directory.CreateDirectory(saveDir);
                WriteInfoMessage("创建下载目录{0}", saveDir);
            }
            Stopwatch _stopwathc = new Stopwatch();
            try
            {
                WriteDebugMessage("准备下载 Host:{0} Port:{1} UserID:{2} Password:{3}", Host, Port, UserID, Password);
                _stopwathc.Start();
                WriteDebugMessage("检查FTP链路");
                //if (ftpClient.IsConnected)
                //{
                //    ftpClient.Disconnect();
                //}

                if (!_ftpClient.IsConnected)
                {
                    _ftpClient.Connect();
                }
                //using (FtpClient client = new FtpClient(Host, Port, new NetworkCredential(UserID, Password)))
                {
                    //, Port, UserID, Password
                    WriteDebugMessage("检查远端文件是否存在");
                    //第一步：判断文件是否存在
                    if (_ftpClient.FileExists(remoteFile))
                    {
                        //第二步：获取文件大小（用于进度展示）
                        long dataLength = _ftpClient.GetFileSize(remoteFile);
                        PushInfoLog(LogOutName, "到FTP服务端获取介质文件{0}大小为{1} {2}字节", remoteFile, FileAssistant.GetLengthStr(dataLength), dataLength);
                        if (dataLength > 1)
                        {
                            ShowInfoMessage("开始下载文件ftp://{0}:{1}/{2}到{3}", Host, Port, remoteFile, saveFile);
                            bool isFileExist = false;
                            if (File.Exists(saveFile))
                            {
                                isFileExist = true;
                                FileInfo info = new FileInfo(saveFile);
                                PushInfoLog(LogOutName, "本地文件{0}已存在，目前大小为{1}", saveFile, info.Length);
                                if (info.Length.Equals(dataLength))
                                {
                                    ShowDebugMessage("经过比对，文件{0}已经正常下载完成，不用重复下载", remoteFile);
                                    return result;
                                }
                            }

                            //小于50M的文件，直接下载
                            if (dataLength < 50 * 1024 * 1024)
                            {
                                //小文件下载
                                FtpStatus ftpStatus = _ftpClient.DownloadFile(saveFile, remoteFile);
                                if (ftpStatus == FtpStatus.Success)
                                {
                                    PushInfoLog(LogOutName, "文件{0}通过小文件下载方式成功！", remoteFile);
                                }
                                else
                                {
                                    PushInfoLog(LogOutName, "文件{0}通过小文件下载方式失败{1}！", remoteFile, ftpStatus);
                                    result.Code = (int)ftpStatus;
                                    result.Message = "不知道为啥失败";
                                }
                            }
                            else
                            {
                                //超过1M的文件下载
                                using (Stream ftpStream = _ftpClient.OpenRead(remoteFile))
                                {
                                    if (ftpStream.Length > 0)
                                    {
                                        //创建本地文件
                                        using (FileStream downStream = new FileStream(saveFile, isFileExist ? FileMode.OpenOrCreate : FileMode.Create))
                                        {

                                            //第三步：创建下载链路
                                            {
                                                FlowAssistantor timer = new FlowAssistantor();
                                                //int _cacheSize = 0;
                                                ////long cl = response1.ContentLength;
                                                ////WriteFtpLog(String.Format("文件{0}大小为{1} {2}", downloadFileName, cl, dataLength));
                                                //int bufferSize = 2048; //2K Byte
                                                int bufferSize = 1 * 1024 * 1024; //1M Byte
                                                //int bufferSize = _cacheSize; //1M Byte
                                                //WriteInfoMessage(string.Format("下载文件流缓存大小为{0}", bufferSize));
                                                long readCount = 0; //每次读取长度 注意用long，不要用int，否则会溢出
                                                int readLength = 0;
                                                byte[] buffer = new byte[bufferSize];
                                                //var output = new byte[bufferSize];
                                                //MD5CryptoServiceProvider md5Provider = new MD5CryptoServiceProvider();
                                                //md5Provider.Initialize();
                                                while ((readLength = ftpStream.Read(buffer, 0, bufferSize)) > 0)
                                                {
                                                    //写盘
                                                    downStream.Write(buffer, 0, readLength);
                                                    //在这里可考虑进行MD5计算
                                                    //md5Provider.TransformBlock(buffer, 0, Convert.ToInt32(readLength), output, 0);
                                                    //将缓存数据写入实体文件
                                                    downStream.Flush();
                                                    //readCount = ftpStream.Read(buffer, 0, bufferSize);
                                                    //已经读取的数量累加
                                                    readCount += readLength;
                                                    timer.ReadSize = readCount;
                                                    //runThread.ReadSize = readCount;
                                                    //runThread.WriteSize = downStream.Position;
                                                    //最大、最小速度判定
                                                    //runThread.MaxRate = timer.FlowSize > 0 & timer.FlowSize > runThread.MaxRate ? timer.FlowSize : runThread.MaxRate;
                                                    //runThread.MinRate = timer.FlowSize > 0 & timer.FlowSize < runThread.MinRate ? timer.FlowSize : runThread.MinRate;
                                                    //计算已经读取的数据百分比
                                                    int percentage = (int)(readCount * 100 / dataLength);
                                                    //UpdateProcess(threadId, percentage);
                                                    //ReportProcess(threadId, dataLength, readCount, downStream.Position, timer.FlowSize);
                                                }
                                                ////完成最后计算，必须调用(由于上一部循环已经完成所有运算，所以调用此方法时后面的两个参数都为0)
                                                //md5Provider.TransformFinalBlock(buffer, 0, 0);
                                                //string md5Str = BitConverter.ToString(md5Provider.Hash).Replace("-", "");
                                                //WriteInfoMessage(String.Format("文件{0}计算后的MD5校验值为{1}", remoteFileName, md5Str));
                                                //md5Provider.Clear();
                                                //downStream.Flush(); //将缓存数据写入实体文件
                                                //result.Sign = md5Str;
                                            }
                                        }
                                    }

                                }
                            }
                        }
                        else
                        {
                            WriteErrMessage("文件{0}在FTP站点{1}上大小不正常", remoteFile, Host);
                        }
                    }
                    else
                    {
                        string putmsg = string.Format("文件{0}在FTP站点{1}上不存在", remoteFile, Host);
                        PushInfoLog(LogOutName, putmsg);
                        throw new RunException((int)TransferCode.FileNotFound, putmsg);
                    }
                }

                //WriteInfoMessage(String.Format("[{0}]文件{1}下载到{2}完成，最大下载速率为{3}/s，最小下载速率为{4}/s，共耗时{5}！", EnumFTPServiceCode.Success,
                //    remoteFileName, saveFileName, GetLengthStr(runThread.MaxRate), GetLengthStr(runThread.MinRate),
                //    _stopwathc.Elapsed.TotalSeconds > 60 ? _stopwathc.Elapsed.TotalMinutes.ToString() + "分" : _stopwathc.Elapsed.TotalSeconds.ToString() + "秒"));
                return result;
            }
            catch (Exception ex)
            {
                WriteErrMessage(ex.ToString());
                return new ResponseLog() { Code = (int)TransferCode.Error,Message = ex.ToString() }; 
                //throw new RunException((int)TransferCode.DownErr, ex.ToString());
            }
            finally
            {
                _stopwathc.Stop();

                FtpReply reply = _ftpClient.LastReply;
                WriteDebugMessage("下载操作完成，FTP回执码 {0} 回执信息 {1}", reply.Code, reply.Message);

                PushInfoLog(LogOutName, "[{0}]文件ftp://{1}下载到{2}完成，共耗时{3}！", reply.Code, Host+"/" + remoteFile, saveFile,
                   FileAssistant.GetTimeStr(_stopwathc.ElapsedMilliseconds));

                _ftpClient.Disconnect();

                ResponseEnd(threadId, reply.Code.ToInt());
            }

            

        }

        /// <summary>
        /// 不关闭链接
        /// </summary>
        /// <remarks>必须和批量下载函数共同使用，不支持下载进度展示</remarks>
        /// <param name="saveFile">本地存储全路径</param>
        /// <param name="remoteFile">远端路径 不包含FTP站点信息</param>
        public ResponseLog DownFiles(string saveFile, string remoteFile)
        {
            WriteDebugMessage("本地存储文件校验");
            saveFile = saveFile.Replace("/", "\\");
            //检查本地存储路径是否正常
            string extName = Path.GetExtension(saveFile);
            if (string.IsNullOrEmpty(extName))
            {
                throw new RunException(TransferCode.ParamsCheckErr, string.Format("文件存储路径校验失败 {0}", saveFile));
            }
            WriteInfoMessage("启动对文件{0}的下载，下载存储全路径为{1}", remoteFile, saveFile);
            //WriteDebugMessage("FTP站点信息为 Host:{0} Port:{1} UserID:{2} Password:{3}", Host, Port, UserID, Password);

            ResponseLog result = new ResponseLog() { Code = (int)TransferCode.Success, Message = "成功" };
            Stopwatch _stopwathc = new Stopwatch();
            try
            {
                _stopwathc.Start();
                WriteDebugMessage("检查远端文件是否存在");
                //第一步：判断文件是否存在
                if (_ftpClient.FileExists(remoteFile))
                {
                    //第二步：获取文件大小（用于进度展示、用于校验本地是否有已下载的文件）
                    long dataLength = _ftpClient.GetFileSize(remoteFile);
                    WriteDebugMessage("到FTP站点获取介质文件{0}大小为{1} {2}字节", remoteFile, FileAssistant.GetLengthStr(dataLength), dataLength);
                    if (dataLength > 1)
                    {
                        bool isFileExist = false;
                        //本地文件校验 若存在则比较大小，大小一致时判定为有效下载
                        if (File.Exists(saveFile))
                        {
                            isFileExist = true;
                            FileInfo info = new FileInfo(saveFile);
                            WriteDebugMessage("本地文件{0}已存在，目前大小为{1}", saveFile, info.Length);
                            if (info.Length.Equals(dataLength))
                            {
                                WriteWarnMessage("经过比对，文件{0}已经正常下载完成，不用重复下载", remoteFile);
                                return result;
                            }
                        }
                        ShowInfoMessage("开始下载文件ftp://{0}:{1}/{2}到{3}", Host, Port, remoteFile, saveFile);
                        //低于100M的文件，直接下载
                        if (dataLength <= 100 * 1024 * 1024)
                        {
                            //小文件下载
                            FtpStatus ftpStatus = _ftpClient.DownloadFile(saveFile, remoteFile);
                            if (ftpStatus == FtpStatus.Success)
                            {
                                ShowInfoMessage("文件{0}通过小文件下载方式成功！", remoteFile);
                            }
                            else
                            {
                                ShowErrMessage("文件{0}通过小文件下载方式失败{1}！", remoteFile, ftpStatus);
                                result.Code = (int)ftpStatus;
                                result.Message = "不知道为啥失败";
                            }
                        }
                        else
                        {
                            //超过100M的文件使用文件流进行下载
                            using (Stream ftpStream = _ftpClient.OpenRead(remoteFile))
                            {
                                if (ftpStream.Length > 0)
                                {
                                    //创建本地文件
                                    using (FileStream downStream = new FileStream(saveFile, isFileExist ? FileMode.OpenOrCreate : FileMode.Create))
                                    {
                                        //第三步：创建下载链路
                                        int bufferSize = 2 * 1024 * 1024; //2M Byte
                                        int readLength = 0;
                                        byte[] buffer = new byte[bufferSize];

                                        while ((readLength = ftpStream.Read(buffer, 0, bufferSize)) > 0)
                                        {
                                            //写盘
                                            downStream.Write(buffer, 0, readLength);
                                            //将缓存数据写入实体文件
                                            downStream.Flush();
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        string putmsg = string.Format("文件{0}在FTP站点{1}上大小不正常", remoteFile, Host);
                        result.Code = (int)TransferCode.FileCheckErr;
                        result.Message = putmsg;
                        WriteErrMessage(putmsg);
                    }
                }
                else
                {
                    string putmsg = string.Format("文件{0}在FTP站点{1}上不存在", remoteFile, Host);
                    result.Code = (int)TransferCode.FileNotFound;
                    result.Message = putmsg;
                    WriteErrMessage(putmsg);
                }
                //WriteInfoMessage(String.Format("[{0}]文件{1}下载到{2}完成，最大下载速率为{3}/s，最小下载速率为{4}/s，共耗时{5}！", EnumFTPServiceCode.Success,
                //    remoteFileName, saveFileName, GetLengthStr(runThread.MaxRate), GetLengthStr(runThread.MinRate),
                //    _stopwathc.Elapsed.TotalSeconds > 60 ? _stopwathc.Elapsed.TotalMinutes.ToString() + "分" : _stopwathc.Elapsed.TotalSeconds.ToString() + "秒"));
                return result;
            }
            catch (Exception ex)
            {
                WriteErrMessage(ex.ToString());
                throw new RunException((int)TransferCode.DownErr, ex.ToString());
            }
            finally
            {
                _stopwathc.Stop();
                FtpReply reply = _ftpClient.LastReply;
                WriteDebugMessage("下载操作完成，回执信息为 [{0}] {1}", reply.Code, reply.Message);
                ShowInfoMessage("[{0}]文件ftp://{1}下载到{2}完成，共耗时{3}毫秒！", reply.Code, Host + "/"+ remoteFile, saveFile,
                    _stopwathc.ElapsedMilliseconds);
            }
        }

        /// <summary>
        /// 批量下载文件
        /// </summary>
        /// <param name="remoteFiles">待下载文件集合</param>
        /// <param name="taskId">任务编号</param>
        /// <returns></returns>
        public ResponseLog DownPatchFiles(List<FileSiteStruct> remoteFiles, string taskId)
        {
            WriteDebugMessage("使用批量下载模式进行入参校验");
            if (remoteFiles == null || remoteFiles.Count == 0)
            {
                //在性能要求高的情况下，不建议抛出异常，使用返回对象
                throw new RunException(TransferCode.ParamsCheckErr);
                //return new ResponseLog() { Code = (int)TransferCode.ParamsCheckErr, Message = "参数检查异常" };
            }
            WriteDebugMessage("检查FTP链路");
            if (_ftpClient == null || _ftpClient.IsDisposed)
            {
                throw new RunException(TransferCode.FtpLinkErr);
                //return new ResponseLog() { Code = (int)TransferCode.Error, Message = "FTP未准备好" };
            }
            ResponseLog result = new ResponseLog() { Code = (int)TransferCode.Success, Message = "成功" };
            WriteDebugMessage(string.Format("{4}待访问FTP站点信息 Host:{0} Port:{1} UserID:{2} Password:{3}", Host, Port, UserID, Password, taskId));

            ShowDebugMessage("任务{1}批量下载操作启动，当前共{0}个文件准备下载", remoteFiles.Count, taskId);
            Stopwatch _stopwathc = new Stopwatch();
            _stopwathc.Start();

            if (!_ftpClient.IsConnected)
            {
                _ftpClient.Connect();
            }

            try
            {
                int i = 1;
                foreach (var item in remoteFiles)
                {
                    if (item.BEnable)
                    {
                        //检查本地目录
                        item.LocalPath.CreateFolder();
                        WriteDebugMessage("任务{2}开始下载第{0}个文件 {1}", i, item.RemotePath, taskId);
                        item.Status = 1;
                        i++;
                        //启动下载
                        // Path.Combine(item.LocalPath, Path.GetFileName(item.RemotePath));
                        ResponseLog response = DownFiles(item.LocalPath, item.RemotePath);
                        item.Status = response.Code == 200 ? 2 : -1;

                        ResponseEnd(item.ID, item.Status);

                        Thread.Sleep(4000);
                    }
                    else
                    {
                        string errInfo = string.Format("任务{1}中介质{0}根据采集规则不予下载", item.RemotePath, taskId);
                        item.Status = 2;
                        WriteDebugMessage(errInfo);
                    }
                }
            }
            //catch (RunException ex)
            //{
            //    WriteErrMessage(ex.ToString());
            //}
            catch (Exception ex)
            {
                WriteErrMessage(ex.ToString());
                throw new RunException((int)TransferCode.DownErr, ex.ToString());
            }
            finally
            {
                _ftpClient.Disconnect();
                _stopwathc.Stop();

                var errTasks = remoteFiles.FindAll(t => t.Status == -1 || t.Status == -2);
                if (errTasks != null && errTasks.Count > 0)
                {
                    errTasks.ForEach(t => WriteErrMessageExt("任务{0}失败项{1}", taskId, t.Name));
                }

                FtpReply reply = _ftpClient.LastReply;
                PushInfoLog(LogOutName, "任务{3}批量下载操作完成，{0}  {1}，共有下载任务{4}，失败{5}耗时{2}！", reply.Code, reply.Message,
                    _stopwathc.Elapsed.TotalSeconds > 60 ? _stopwathc.Elapsed.TotalMinutes.ToString() + "分" : _stopwathc.Elapsed.TotalSeconds.ToString() + "秒",
                    taskId, remoteFiles.Count, errTasks.Count);
            }

            return result;
        }

        /// <summary>
        /// 获取目录下所有数据
        /// </summary>
        /// <param name="remoteDirectorie">远端目录</param>
        /// <param name="isSub">是否包含子目录 默认包含子目录</param>
        /// <returns></returns>
        public List<FileSiteStruct> GetDirectorieFile(string remoteDirectorie, bool isSub = true)
        {
            WriteInfoMessage("进入FluentFtp目录下载线程");
            ResponseLog result = new ResponseLog() { Code = (int)TransferCode.Success, Message = "成功" };

            if (_ftpClient == null || _ftpClient.IsDisposed)
            {
                //return new ResponseLog() { Code = (int)EnumFTPServiceCode.Error, Message = "FTP未准备好" };
                return null;
            }
            WriteDebugMessage("检查本地下载目录");

            FtpListOption option = isSub ? FtpListOption.Recursive : FtpListOption.ForceList;
            var fileList = new List<FileSiteStruct>();

            try
            {
                fileList.AddRange(ListAllFiles(remoteDirectorie));
                //DownFiles(saveDir, fileList);
            }
            catch (Exception ex)
            {
                WriteErrMessage(ex.ToString());
            }

            return fileList;
        }

        /// <summary>
        /// 采集指定目录下所有数据
        /// </summary>
        /// <param name="rootDir">检索目录</param>
        /// <param name="isSub">是否包含子目录</param>
        /// <param name="ignoreDir">例外目录</param>
        /// <returns></returns>
        public List<FileSiteStruct> ListAllFiles(string rootDir, bool isSub = false, string ignoreDir = "")
        {
            if (_ftpClient == null || _ftpClient.IsDisposed)
            {
                return null;
            }

            var fileList = new List<FileSiteStruct>();
            try
            {
                FtpListItem[] listItem = _ftpClient.GetListing(rootDir, FtpListOption.ForceList);
                foreach (var item in listItem)
                {
                    switch (item.Type)
                    {
                        case FtpObjectType.Directory:
                            if (isSub)
                            {
                                fileList.AddRange(ListAllFiles(item.FullName, isSub));
                            }
                            break;
                        case FtpObjectType.File:
                            string tmpExtName = Path.GetExtension(item.FullName);
                            fileList.Add(new FileSiteStruct()
                            {
                                ID = Guid.NewGuid().ToString("N"),
                                IsDirectory = false,
                                CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                                Name = item.Name,
                                FileExtName = tmpExtName,
                                Size = item.Size,
                                RemotePath = item.FullName
                            });
                            break;
                    }
                }
            }
            catch (Exception ex)
            {
                WriteErrMessage(ex.ToString());
            }

            return fileList;
        }

        #region 文件上传
        /// <summary>
        /// 字节模式上传文件
        /// </summary>
        /// <remarks>用于大文件的上传</remarks>
        /// <param name="upFile">本地文件信息</param>
        /// <param name="remoteDirectory">远端上传目录</param>
        /// <param name="allowGuid">GUID命名</param>
        /// <param name="allowTmpName">TMP命名</param>
        /// <param name="isOver">覆盖模式</param>
        private void Upload4Byte(FileInfo upFile, string remoteDirectory, string threadId = "", bool allowGuid = false, bool allowTmpName = true, bool isOver = false)
        {
            using (FileStream upStream = upFile.OpenRead())
            {
                //获取文件长度
                long dataLength = upStream.Length;
                if (dataLength > 0)
                {
                    FlowAssistantor timer = new FlowAssistantor();
                    int bufferSize = 2 * 1024 * 1024; //2K Byte
                    long readCount = 0; //每次读取长度
                    int readLength = 0;
                    byte[] buffer = new byte[bufferSize];

                    if (_ftpClient == null || _ftpClient.IsDisposed)
                    {
                        throw new RunException((int)TransferCode.AuthFailed, "FTP服务链接异常");
                    }
                    if (!_ftpClient.IsConnected)
                    {
                        _ftpClient.Connect();
                    }
                    string tmpFileName = allowGuid ? string.Concat(Guid.NewGuid().ToString("N"), upFile.Extension, ".tmp") : string.Concat(upFile.Name, ".tmp");
                    string newFileName = allowGuid ? string.Concat(Guid.NewGuid().ToString("N"), upFile.Extension) : upFile.Name;
                    string remoteFileName = string.Concat(remoteDirectory, "/", allowTmpName ? tmpFileName : newFileName);
                    {
                        if (!_ftpClient.DirectoryExists(remoteDirectory))
                        {
                            WriteDebugMessage("远端目录{0}不存在，创建远端目录", remoteDirectory);
                            bool isSuccess = _ftpClient.CreateDirectory(remoteDirectory);
                        }

                        WriteDebugMessage("设置远端工作目录为{0}", remoteDirectory);
                        _ftpClient.SetWorkingDirectory(remoteDirectory);
                        var ftpRemodeExistsMode = isOver ? FtpRemoteExists.Overwrite : FtpRemoteExists.Skip;
                        //定位到第一个字节
                        long tmpadd = upStream.Seek(0, SeekOrigin.Begin);
                        WriteDebugMessage("当前文件{0}读取位置为{1}", remoteFileName, tmpadd.ToString());

                        while ((readLength = upStream.Read(buffer, 0, bufferSize)) > 0)
                        {
                            //Action<FtpProgress> tt = null;
                            //采取字节模式上传
                            FtpStatus status = _ftpClient.UploadBytes(buffer, remoteFileName, FtpRemoteExists.Overwrite);
                            if (status == FtpStatus.Success)
                            {
                                readCount += readLength;
                                timer.ReadSize = readCount;
                            }
                            else
                            {
                                WriteErrMessage("上传有失败字节{0}", status.ToString());
                                //throw new RunException((int)TransferCode.PermissionDenied, "权限不足");
                            }

                            //计算已经读取的数据百分比
                            int percentage = (int)(readCount * 100 / dataLength);
                            Debug.WriteLine(percentage);
                            //UpdateUpProcess(threadId, percentage);
                            //ReportUpProcess(threadId, dataLength, readCount, upStream.Position, timer.FlowSize);
                        }
                        WriteInfoMessage("文件上传完成");
                        if (allowTmpName)
                        {
                            _ftpClient.Rename(remoteFileName, remoteFileName.Replace(".tmp", ""));
                        }

                    }
                }
            }
        }

        private void Upload4Stream(FileInfo upFile, string remoteDirectory, bool allowGuid = false, bool allowTmpName = true, bool isOver = false)
        {
            //小文件采取IO流模式上传
            using (FileStream fs = upFile.OpenRead())
            {
                if (_ftpClient == null || _ftpClient.IsDisposed)
                {
                    throw new RunException((int)TransferCode.AuthFailed, "FTP服务链接异常");
                }
                if (!_ftpClient.IsConnected)
                {
                    _ftpClient.Connect();
                }
                string newFileName = allowGuid ? string.Concat(Guid.NewGuid().ToString("N"), allowTmpName ? ".tmp" : upFile.Extension) : allowTmpName ? string.Concat(upFile.Name, ".tmp") : upFile.Name;
                string remoteFileName = string.Concat(remoteDirectory, "/", newFileName);
                {
                    if (!_ftpClient.DirectoryExists(remoteDirectory))
                    {
                        bool isSuccess = _ftpClient.CreateDirectory(remoteDirectory);
                    }
                    _ftpClient.SetWorkingDirectory(remoteDirectory);
                    var ftpRemodeExistsMode = isOver ? FtpRemoteExists.Overwrite : FtpRemoteExists.Skip;
                    FtpStatus status = _ftpClient.UploadStream(fs, remoteFileName, ftpRemodeExistsMode, true);
                    if (status == FtpStatus.Success)
                    {
                        WriteInfoMessage("文件上传完成");
                    }
                    else
                    {
                        throw new RunException((int)TransferCode.PermissionDenied, "权限不足");
                    }

                }
            }
        }

        /// <summary>
        /// 上传单个文件
        /// </summary>
        /// <param name="localFile">本地文件全路径（绝对路径）</param>
        /// <param name="remoteDirectory">FTP服务上目录（相对路径）</param>
        /// <param name="allowGuid">使用GUID命名规范</param>
        public void UploadFile(string localFile, string remoteDirectory, bool allowGuid = false, bool allowTmpName = true, bool isOver = false)
        {
            if (!string.IsNullOrEmpty(localFile) && !string.IsNullOrEmpty(remoteDirectory))
            {
                WriteDebugMessage(string.Format("准备上传文件{0}到{1}{2}", localFile, RemotePath, remoteDirectory));
                if (!File.Exists(localFile))
                {
                    WriteErrMessage(string.Format("文件{0}不存在，请确认", localFile));
                    return;
                }
                FileInfo tmpFile = new FileInfo(localFile);
                WriteDebugMessage(string.Format("文件{0}大小为{1}", localFile, FileAssistant.GetLengthStr(tmpFile.Length)));
                //ShowUpName(string.Format("开始上传文件 {0}...", tmpFile.Name.Length > 18 ? tmpFile.Name.Remove(16) : tmpFile.Name));
                if (tmpFile.Length > 100 * 1024 * 1024)
                {
                    WriteDebugMessage(string.Format("文件{0}大于100M，将使用字节模式进行上传", localFile));
                    //大文件采取字节模式上传
                    Upload4Byte(tmpFile, remoteDirectory, "", allowGuid, allowTmpName, isOver);
                }
                else
                {
                    WriteDebugMessage(string.Format("文件{0}小于100M，将使用IO流模式进行上传", localFile));
                    //小文件采取IO流模式上传
                    Upload4Stream(tmpFile, remoteDirectory, allowGuid, allowTmpName, isOver);
                }


            }
            else
            {
                throw new RunException((int)TransferCode.AddressErr, "文件校验失败");
            }
        }

        /// <summary>
        /// 上传文件集号
        /// </summary>
        public void UploadFiles()
        {

        }

        /// <summary>
        /// 上传一个目录
        /// </summary>
        /// <param name="directory"></param>
        public void UploadDirectory(string directory)
        {

        }
        #endregion 文件上传

        #region 输出消息
        /// <summary>
        /// 更新进度条
        /// </summary>
        /// <param name="threadId">任务编号</param>
        /// <param name="progress">进度</param>
        private void UpdateDownProcess(string threadId, int progress)
        {
            DownloadProcessChangeEvent?.Invoke(threadId, progress);
        }

        /// <summary>
        /// 更新进度（处理了多少字节）
        /// </summary>
        /// <param name="id">线程序号</param>
        /// <param name="size">文件大小</param>
        /// <param name="progress">读取进度</param>
        /// <param name="position">写入进度</param>
        /// <param name="flowsize">速率</param>
        private void ReportDownProcess(string id, long size, long progress, long position, long flowsize)
        {
            DownloadChangeEvent?.Invoke(id, size, progress, position, flowsize);
        }

        private void ShowUpName(string runName)
        {
            //UploadMessageEvent?.Invoke(runName);
        }

        /// <summary>
        /// 更新进度条
        /// </summary>
        /// <param name="threadId"></param>
        /// <param name="progress"></param>
        private void UpdateUpProcess(string threadId, int progress)
        {
            //UploadProgressChangeEvent?.Invoke(threadId, progress);
        }

        /// <summary>
        /// 更新进度（处理了多少字节）
        /// </summary>
        /// <param name="id">线程序号</param>
        /// <param name="size">文件大小</param>
        /// <param name="progress">读取进度</param>
        /// <param name="position">写入进度</param>
        /// <param name="flowsize">速率</param>
        private void ReportUpProcess(string id, long size, long progress, long position, long flowsize)
        {
            //UploadChangeEvent?.Invoke(id, size, progress, position, flowsize);
        }

        /// <summary>
        /// 单个文件下载完成事件
        /// </summary>
        /// <param name="fileId"></param>
        /// <param name="state"></param>
        private void ResponseEnd(string fileId, int state)
        {
            if (DownloadEndEvent != null)
            {
                WriteDebugMessage("下载完成调用异步回执{0}  {1}", fileId, state.ToString());
                DownloadEndEvent(fileId, state);
            }
            else
            {
                WriteWarnMessage("下载完成不调用异步回执{0}  {1}", fileId, state.ToString());
            }
            //DownloadEndEvent?.Invoke(fileId, state);
        }

        #endregion 输出消息

        /// <summary>
        /// 析构函数
        /// </summary>
        public void Dispose()
        {
            WriteWarnMessage("进入销毁方法");
            WriteWarnMessage("==================================================");
            if (_ftpClient != null)
            {
                if (_ftpClient.IsConnected)
                {
                    _ftpClient.Disconnect();
                }
                if (!_ftpClient.IsDisposed)
                {
                    _ftpClient.Dispose();
                }
                _ftpClient = null;
            }
        }
    }
}
